import lockfile from 'proper-lockfile'
import path from 'path'
import superFs from 'fs-extra'
import dayjs from 'dayjs'
import * as portfinder from 'portfinder'
import { logger } from '../until/log'

interface DatabaseInstance {
    database: { [key: string]: instanceRegistry },
    nextInstanceNumber: number
}
// 端口配置接口
interface PortConfig {
    masterPort: number,
    childPort: number,
    websocketPort: number,
    allocatedAt: string
}

// 应用实例注册
interface instanceRegistry {
    instanceId: any,
    isActive: boolean,
    pid: number, // 进程的id
    dataPath: string,
    lastUsed: string,
    createdAt: string,
    ports?: PortConfig // 端口配置信息
}
class instanceRegistryManager {
    // 单列模式实例管理器
    private static instance: instanceRegistryManager;

    private instanceRegistrJsonPath: string; // 实例注册表所在位置
    public static get getInstance() {
        if (!this.instance) {
            this.instance = new instanceRegistryManager();
        }
        return this.instance;
    }

    constructor() {
        // 检查是否在 pkg 打包环境中
        if ((process as any).pkg) {
            // 在打包环境中，使用 process.cwd() 作为基础路径
            this.instanceRegistrJsonPath = path.join(process.cwd(), 'app', 'config', 'instance.json');
        } else {
            // 在开发环境中，使用相对于当前文件的路径
            this.instanceRegistrJsonPath = path.join(process.cwd(), 'app', 'config', 'instance.json');
        }
    }

    // 使用portfinder查找可用端口
    private async findAvailablePort(startPort: number = 3000): Promise<number> {
        try {
            const port = await portfinder.getPortPromise({
                port: startPort,
                stopPort: 65535
            });
            
            // 额外检查端口是否在注册表中被标记为使用
            const isRegistryInUse = await this.isPortInRegistry(port);
            if (isRegistryInUse) {
                // 如果在注册表中被占用，递归查找下一个可用端口
                return await this.findAvailablePort(port + 1);
            }
            
            return port;
        } catch (error) {
            console.error('查找可用端口失败:', error);
            throw new Error(`无法找到可用端口，起始端口: ${startPort}`);
        }
    }

    // 检查端口是否在注册表中被占用
    private async isPortInRegistry(port: number): Promise<boolean> {
        try {
            const registry = await this.readRegistryJson();
            for (const instanceName in registry.database) {
                const instance = registry.database[instanceName];
                if (instance.ports && instance.isActive) {
                    if (instance.ports.masterPort === port || 
                        instance.ports.childPort === port || 
                        instance.ports.websocketPort === port) {
                        return true;
                    }
                }
            }
            return false;
        } catch (error) {
            console.error('检查注册表端口占用状态失败:', error);
            return false;
        }
    }

    // 分配一组可用端口
    private async allocatePortSet(): Promise<PortConfig> {
        try {
            // 使用portfinder分配主端口
            const masterPort = await this.findAvailablePort(3000);
            
            // 分配子端口（从主端口+1开始查找）
            const childPort = await this.findAvailablePort(masterPort + 1);
            
            // 分配WebSocket端口（从子端口+1开始查找）
            const websocketPort = await this.findAvailablePort(childPort + 1);
            
            const portConfig = {
                masterPort,
                childPort,
                websocketPort,
                allocatedAt: dayjs().format('YYYY-MM-DD HH:mm:ss')
            };
            return portConfig;
            
        } catch (error) {
            console.error('分配端口组失败:', error);
            throw new Error(`端口分配失败: ${error instanceof Error ? error.message : String(error)}`);
        }
    }

    // 为实例分配或复用端口
    public async allocatePortsForInstance(instanceName: string): Promise<PortConfig> {
        const registry = await this.readRegistryJson();
        const instance = registry.database[instanceName];
        
        if (!instance) {
            throw new Error(`实例 ${instanceName} 不存在`);
        }
        
        // 如果实例已有端口配置且实例处于活跃状态，直接返回
        if (instance.ports && instance.isActive) {
            return instance.ports;
        }
        
        // 分配新的端口组
        const newPorts = await this.allocatePortSet();
        console.log(`实例 ${instanceName} 分配新端口:`, newPorts);
        
        // 更新实例的端口配置
        registry.database[instanceName].ports = newPorts;
        registry.database[instanceName].isActive = true;
        await this.writeRegistryJson(registry);
        
        return newPorts;
    }

    // 释放实例端口（将实例标记为非活跃）
    public async releaseInstancePorts(instanceName: string): Promise<void> {
        const registry = await this.readRegistryJson();
        const instance = registry.database[instanceName];
        
        if (instance) {
            instance.isActive = false;
            instance.lastUsed = dayjs().format('YYYY-MM-DD HH:mm:ss');
            console.log(`实例 ${instanceName} 端口已释放，标记为非活跃状态`);
            await this.writeRegistryJson(registry);
        }
    }

    // 根据PID释放端口
    public async releasePortsByPid(pid: number): Promise<void> {
        const registry = await this.readRegistryJson();
        
        for (const instanceName in registry.database) {
            const instance = registry.database[instanceName];
            if (instance.pid === pid && instance.isActive) {
                await this.releaseInstancePorts(instanceName);
                break;
            }
        }
    }
    // 读取配置文件
    public async readRegistryJson(): Promise<DatabaseInstance> {
        try {
            // 1. 检测是否存在注册表
            if (!superFs.existsSync(this.instanceRegistrJsonPath)) {
                console.log('注册表文件不存在，创建默认注册表');
                const defaultRegistry = this.getDefaultRegistryJson();
                await this.writeRegistryJson(defaultRegistry);
                return defaultRegistry;
            }

            // 2. 注册表存在但是它是空表
            if (superFs.statSync(this.instanceRegistrJsonPath).size === 0) {
                console.log('注册表文件为空，写入默认内容');
                const defaultRegistry = this.getDefaultRegistryJson();
                await this.writeRegistryJson(defaultRegistry);
                return defaultRegistry;
            }

            // 3.读取注册表并且锁表
            await lockfile.lock(this.instanceRegistrJsonPath, { retries: 10, stale: 10000, wait: 1000 });
            
            let registry: DatabaseInstance;
            try {
                registry = await superFs.readJson(this.instanceRegistrJsonPath) as DatabaseInstance;
                
                // 验证注册表结构是否正确
                if (!registry || typeof registry !== 'object' || !registry.database || typeof registry.nextInstanceNumber !== 'number') {
                    throw new Error('注册表结构无效');
                }
            } catch (parseError: any) {
                 console.warn('注册表文件格式错误，重新创建:', parseError.message);
                // 解锁文件
                await lockfile.unlock(this.instanceRegistrJsonPath);
                
                // 备份损坏的文件
                const backupPath = this.instanceRegistrJsonPath + '.backup.' + Date.now();
                try {
                    await superFs.copy(this.instanceRegistrJsonPath, backupPath);
                    console.log(`已备份损坏的注册表文件到: ${backupPath}`);
                } catch (backupError: any) {
                     console.warn('备份损坏文件失败:', backupError.message);
                }
                
                // 重新创建默认注册表
                const defaultRegistry = this.getDefaultRegistryJson();
                await this.writeRegistryJson(defaultRegistry);
                return defaultRegistry;
            }

            // 4.解锁
            await lockfile.unlock(this.instanceRegistrJsonPath);
            return registry;
        } catch (err) {
            console.error('读取注册表时发生错误:', err);
            
            // 尝试解锁（如果已锁定）
            try {
                if (await lockfile.check(this.instanceRegistrJsonPath)) {
                    await lockfile.unlock(this.instanceRegistrJsonPath);
                }
            } catch (unlockError: any) {
                 console.warn('解锁文件失败:', unlockError.message);
            }
            
            // 返回默认注册表
            const defaultRegistry = this.getDefaultRegistryJson();
            try {
                await this.writeRegistryJson(defaultRegistry);
            } catch (writeError: any) {
                 console.error('写入默认注册表失败:', writeError.message);
            }
            return defaultRegistry;
        }
    }

    public getDefaultRegistryJson(): DatabaseInstance {
        return {
            database: {},
            nextInstanceNumber: 1
        }
    }

    // 写注册表信息
    public async writeRegistryJson(registry: DatabaseInstance) {
        try {
            // 1. 判断这个全局配置文件是否存在
            if (!superFs.existsSync(this.instanceRegistrJsonPath)) {
                return superFs.writeJson(this.instanceRegistrJsonPath, registry);
            }
            
            await lockfile.lock(this.instanceRegistrJsonPath, { retries: 10, stale: 10000, wait: 1000 })
            await superFs.writeJson(this.instanceRegistrJsonPath, registry);
            await lockfile.unlock(this.instanceRegistrJsonPath);
        } catch(err) {
            console.error('Error writing database registry:', err);
        }
    }

    // 进行实例注册
    public async registerApplication(instance: instanceRegistry): Promise<string> {
        try {
            // 1. 先检查并清理已停止的实例
            await this.checkAndUpdateInstanceStatus();
            
            let registry: DatabaseInstance = await this.readRegistryJson();
            
            // 2. 查询是否有可以复用的实例
            let reuseInstanceName = await this.findBeforeInstanceId();
            
            if (reuseInstanceName) {
                
                // 为复用的实例分配新的端口
                const portConfig = await this.allocatePortSet();
                
                registry.database[reuseInstanceName] = {
                    ...instance,
                    instanceId: registry.database[reuseInstanceName].instanceId,
                    dataPath: registry.database[reuseInstanceName].dataPath, // 保持原有的数据库路径
                    ports: portConfig
                };
                await this.writeRegistryJson(registry);
                return reuseInstanceName;
            } else {
                // 创建新实例
                const instanceName = "database-" + new String(registry.nextInstanceNumber).padStart(2, '0');
                console.log(`创建新实例: ${instanceName}`);
                
                // 为新实例分配端口
                const portConfig = await this.allocatePortSet();
                
                // 构建数据库路径
                let dataPath: string;
                if ((process as any).pkg) {
                    // 在打包环境中，数据库文件放在 exe 同级目录
                    dataPath = path.join(process.cwd(), 'app', 'config', instanceName + '.sqlite');
                } else {
                    // 在开发环境中，使用项目根目录
                    dataPath = path.join(process.cwd(), 'app', 'config', instanceName + '.sqlite');
                }
                
                registry.database[instanceName] = {
                    ...instance,
                    instanceId: registry.nextInstanceNumber,
                    dataPath: dataPath,
                    ports: portConfig
                };
                
                await this.writeRegistryJson(registry);
                console.log(`创建新实例: ${instanceName}，分配端口:`, portConfig);
                // 调整下一个指针
                await this.nextInstanceNumber();
                return instanceName;
            }

        } catch (err) {
            console.error('Error registering application:', err);
            throw err;
        }
    }

    // 修改这个实例的状态
    public async changeApplicationStatus(pid) {
        const registryList:DatabaseInstance = await this.readRegistryJson()
        for (let instanceName in registryList.database) {
            if (registryList.database[instanceName].pid === pid) {
                registryList.database[instanceName].isActive = false;
                registryList.database[instanceName].lastUsed = dayjs().format('YYYY-MM-DD HH:mm:ss');
                await this.writeRegistryJson(registryList);
                break; // 找到后退出循环
            }
        }
    }

    // 根据PID标记实例为非活跃状态
    public async markInstanceAsInactiveByPid(pid: number): Promise<boolean> {
        try {
            const registryList: DatabaseInstance = await this.readRegistryJson();
            let found = false;
            
            for (let instanceName in registryList.database) {
                if (registryList.database[instanceName].pid === pid && registryList.database[instanceName].isActive) {
                    registryList.database[instanceName].isActive = false;
                    found = true;
                    console.log(`标记实例 ${instanceName} (PID: ${pid}) 为非活跃状态`);
                }
            }
            
            if (found) {
                await this.writeRegistryJson(registryList);
            }
            
            return found;
        } catch (err) {
            console.error('标记实例为非活跃状态时出错:', err);
            return false;
        }
    }

    // 检查进程是否存活
    private isProcessAlive(pid: number): boolean {
        try {
            // 使用 process.kill(pid, 0) 检查进程是否存在
            // 如果进程存在，不会发送信号，只是检查权限
            // 如果进程不存在，会抛出 ESRCH 错误
            process.kill(pid, 0);
            return true;
        } catch (err: any) {
            if (err.code === 'ESRCH') {
                // 进程不存在
                return false;
            } else if (err.code === 'EPERM') {
                // 没有权限，但进程存在
                return true;
            } else {
                // 其他错误，假设进程不存在
                return false;
            }
        }
    }

    // 检查所有实例的存活状态并更新注册表
    public async checkAndUpdateInstanceStatus(): Promise<void> {
        try {
            const registryList: DatabaseInstance = await this.readRegistryJson();
            let hasChanges = false;
            
            for (let instanceName in registryList.database) {
                const instance = registryList.database[instanceName];
                
                if (instance.isActive && !this.isProcessAlive(instance.pid)) {
                    console.log(`检测到实例 ${instanceName} (PID: ${instance.pid}) 已停止运行，更新状态为非活跃`);
                    instance.isActive = false;
                    hasChanges = true;
                }
            }
            
            if (hasChanges) {
                await this.writeRegistryJson(registryList);
                console.log('实例状态检查完成，已更新注册表');
            }
        } catch (err) {
            console.error('检查实例状态时出错:', err);
        }
    }

    // 获取当前活跃实例数量
    public async getActiveInstanceCount(): Promise<number> {
        try {
            const registryList: DatabaseInstance = await this.readRegistryJson();
            let count = 0;
            
            for (let instanceName in registryList.database) {
                if (registryList.database[instanceName].isActive) {
                    count++;
                }
            }
            
            return count;
        } catch (err) {
            console.error('获取活跃实例数量时出错:', err);
            return 0;
        }
    }

    // 启动定期检查任务
    public startPeriodicCheck(intervalMs: number = 30000): NodeJS.Timeout {
        console.log(`启动实例状态定期检查，间隔: ${intervalMs}ms`);
        return setInterval(async () => {
            await this.checkAndUpdateInstanceStatus();
        }, intervalMs);
    }

    // 是否引用之前的这个应用实例
    public async findBeforeInstanceId(): Promise<string> {
        const registryList:DatabaseInstance = await this.readRegistryJson()
        // 优先查找非活跃的实例进行复用
        for (let instance in registryList.database) {
            if (!registryList.database[instance].isActive) {
                return instance
            }
        }
        // 如果没有非活跃实例，返回空字符串表示需要创建新实例
        return ''
    }

    // 查找一个活跃的实例
    public async findActiveInstance(): Promise<string> {
        const registryList:DatabaseInstance = await this.readRegistryJson()
        for (let instance in registryList.database) {
            if (registryList.database[instance].isActive) {
                return instance
            }
        }
        return ''
    }

    // 激活指定的实例
    public async activateInstance(instanceName: string): Promise<void> {
        try {
            await lockfile.lock(this.instanceRegistrJsonPath, { retries: 10, stale: 10000, wait: 1000 });
            
            let registry: DatabaseInstance = await this.readRegistryJson();
            
            if (registry.database[instanceName]) {
                registry.database[instanceName].isActive = true;
                registry.database[instanceName].lastUsed = new Date().toLocaleString('zh-CN');
                registry.database[instanceName].pid = process.pid;
                
                await this.writeRegistryJson(registry);
                console.log(`实例 ${instanceName} 已激活`);
            } else {
                throw new Error(`实例 ${instanceName} 不存在`);
            }
            
            await lockfile.unlock(this.instanceRegistrJsonPath);
        } catch (err) {
            console.error(`激活实例 ${instanceName} 失败:`, err);
            // 确保在出错时也释放锁
            try {
                await lockfile.unlock(this.instanceRegistrJsonPath);
            } catch (unlockError: any) {
                console.warn('解锁文件失败:', unlockError.message);
            }
            throw err;
        }
    }

    // 增加这个下一个实例的指针
    public async nextInstanceNumber() {
        try {
            let registry: DatabaseInstance = await this.readRegistryJson();
            registry.nextInstanceNumber++;
            this.writeRegistryJson(registry);
        } catch (err) {
            console.error('Error incrementing next instance number:', err);
        }
    }

}

export default instanceRegistryManager;